package pso.velocity;

import java.util.logging.Logger;

import pso.particle.Particle;
import evolution.problem.ProblemConstraints;
import evolution.random.RandomGenerator;
import evolution.random.SFMT19937RandomGen;

public abstract class AbstractVelocityUpdateStrategy implements
		VelocityUpdateStrategy {

	private static final Logger log = Logger
			.getLogger(AbstractVelocityUpdateStrategy.class.getCanonicalName());
	protected RandomGenerator randomGenerator = new SFMT19937RandomGen();
	protected double cognitiveFactor = 2.05;
	protected double socialFactor = 2.05;
	protected double maximumVelocity = Double.POSITIVE_INFINITY;

	@Override
	public void updateVelocities(Particle[] particles) {
		for (int i = 0; i < particles.length; i++) {
			Particle particle = particles[i];

			double[] position = particle.getPosition();
			int dimensions = position.length;
			double[] newVelocity = new double[dimensions];

			double[] bestNeighborhoodPosition = particle.getNeighborhood()
					.getBestPosition();
			double[] bestParticlePosition = particle.getBestPosition();
			double[] currentVelocity = particle.getVelocity();
			// log.info("Current velocity: " + Arrays.toString(position));

			for (int j = 0; j < dimensions; j++) {
				double cognitivePart = cognitiveFactor
						* randomGenerator.nextDouble()
						* (bestParticlePosition[j] - position[j]);
				double socialPart = socialFactor * randomGenerator.nextDouble()
						* (bestNeighborhoodPosition[j] - position[j]);

				newVelocity[j] = limitNewVelocity(calculateNewVelocity(
						currentVelocity[j], cognitivePart, socialPart));
			}

			particle.setVelocity(newVelocity);
			// log.info("New velocity: " + Arrays.toString(position));
		}

	}

	/**
	 * Implement this method to perform specific calculations of new velocity
	 * 
	 * @param currentVelocity
	 * @param cognitivePart
	 * @param socialPart
	 * @return implementation specific velocity value
	 */
	public abstract double calculateNewVelocity(double currentVelocity,
			double cognitivePart, double socialPart);

	public double limitNewVelocity(double calculatedVelocity) {
		if (calculatedVelocity > maximumVelocity) {
			return maximumVelocity;
		}
		if (calculatedVelocity < -maximumVelocity) {
			return -maximumVelocity;
		}
		return calculatedVelocity;
	}

	public void configureMaximumVelocity(ProblemConstraints problemConstraints) {
		maximumVelocity = (problemConstraints.getMaxArg() - problemConstraints
				.getMinArg()) / 2;
	}

	public double getCognitiveFactor() {
		return cognitiveFactor;
	}

	public void setCognitiveFactor(double cognitiveFactor) {
		this.cognitiveFactor = cognitiveFactor;
	}

	public double getSocialFactor() {
		return socialFactor;
	}

	public void setSocialFactor(double socialFactor) {
		this.socialFactor = socialFactor;
	}

	public RandomGenerator getRandomGenerator() {
		return randomGenerator;
	}

	public void setRandomGenerator(RandomGenerator randomGenerator) {
		this.randomGenerator = randomGenerator;
	}

	public double getMaximumVelocity() {
		return maximumVelocity;
	}

	public void setMaximumVelocity(double maximumVelocity) {
		this.maximumVelocity = maximumVelocity;
	}
}
